﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using CE.iPhone.PList;
using cczEdit.DramaDialog;
namespace cczEdit.Data
{
    class CodeStyleUnit
    {
        public String instructDspt = "";
        public int instructBytes = 0;
        public int paramNum = 0;
        public List<String> paramList;
        public Dictionary<int, Dictionary<int, String>> enumVarList;
        public CodeStyleUnit()
        {
            paramList = new List<String>();
            enumVarList = new Dictionary<int, Dictionary<int, string>>();
        }
    }
    enum ParentDictType { 
        Story=0,
        Council,
        Trigger
    }
    public class FileData
    {
        PListDict rootDict;
        public static bool isRDrama;
        public static String fileName;
        public FileInfo CurrentFile;
        public FileStream CurrentStream;
        InstructMap instructMap;
        //Code Config file Data
        Dictionary<int, CodeStyleUnit> styleDic;
        //Scene offset
        List<int> sceneOffsets;

        public FileData()
        {
        }

        public void LoadData(string fileName_,PListDict rootDict_)
        {
            fileName = fileName_;
            try
            {
                CurrentFile = new FileInfo(fileName_);
                CurrentStream = CurrentFile.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
            }
            catch (Exception ex)
            {
                System.Console.WriteLine(ex.Message);
                CurrentStream = null;
                return;
            }
            if (fileName.Contains("R_"))
            {
                isRDrama = true;
            }
            else
                isRDrama = false;
            rootDict = rootDict_;
            instructMap = new InstructMap(rootDict);
            ReadCodeStyle();
            LoadSceneOffset();
            foreach (int sceneOffset in sceneOffsets)
            {
                ReadSceneByOffset(sceneOffset);
            }
            
        }
        //Read Code Config File
        private void ReadCodeStyle()
        {
            styleDic = new Dictionary<int, CodeStyleUnit>();
            String l = String.Empty;
            using (StreamReader reader = new StreamReader(Form1.cczDirector + "/style.txt",Encoding.GetEncoding("gb2312")))
            {
                l = reader.ReadLine();

                List<String> lines = new List<String>();

                while (l != null)
                {
                    lines.Add(l);
                    l = reader.ReadLine();
                }
                int lineNum = 1;
                CodeStyleUnit curCodeStyleUnit = new CodeStyleUnit();
                int curInstructID = 0;
                int index = 0;
                while (index < lines.Count)
                {
                    String line = lines[index];
                    if (line == "")
                    {
                        
                        styleDic.Add(curInstructID, curCodeStyleUnit);
                        curCodeStyleUnit = new CodeStyleUnit();
                        lineNum = 1;
                        ++index;
                        continue;
                    }
                    else if (lineNum == 1)
                    {
                        curInstructID=Int32.Parse(line.ToString());

                    }
                    else if (lineNum == 2)
                    {
                        curCodeStyleUnit.instructDspt = line;
                    }
                    else if (lineNum == 3)
                    {
                        curCodeStyleUnit.instructBytes = Int32.Parse(line.ToString());
                    }
                    else if (lineNum == 4)
                    {
                        curCodeStyleUnit.paramNum = Int32.Parse(line.ToString());
                    }
                    else
                    {
                        int temp;
                        if (int.TryParse(line, out temp) == true && int.TryParse(lines[index+1], out temp) == true) //判断是否可以转换为整型
                        {
                            //enum
                            index += Int32.Parse(line) * 2 + 1;
                            lineNum += Int32.Parse(line) * 2 + 1;
                            continue;
                        }
                        else
                        {
                            curCodeStyleUnit.paramList.Add(line);
                        }
                    }

                    ++lineNum;
                    ++index;
                }
                styleDic.Add(curInstructID, curCodeStyleUnit);

            }
        }
        //Load Scene Offset
        private void LoadSceneOffset()
        {
            sceneOffsets = new List<int>();
            var bytesArray = new byte[4];
            int sceneCnfOffset = 10;
            while(true)
            {
                CurrentStream.Seek(sceneCnfOffset, SeekOrigin.Begin);
                CurrentStream.Read(bytesArray, 0, 4);
                if (BitConverter.ToInt32(bytesArray, 0) > 65535)
                {
                    break;
                }
                else
                {
                    sceneOffsets.Add(BitConverter.ToInt32(bytesArray, 0));
                }
                sceneCnfOffset += 4;
            }
        }
        private void ReadSceneByOffset(int sceneOffset)
        {
            CurrentStream.Seek(sceneOffset, SeekOrigin.Begin);
            var unit = new byte[2];
            CurrentStream.Read(unit, 0, 2);
            int sectionAmount = BitConverter.ToInt16(unit,0);
            PListArray parentDict;
            ParentDictType parentDictType;
            if(sectionAmount<=0)
                return;
            else if (sectionAmount == 1)
            {
                if (isRDrama&&((PListArray)rootDict["Council"]).Count > 0)
                {
                    parentDict = (PListArray)rootDict["SpecialStory"];

                }
                else
                {
                    parentDict = (PListArray)rootDict["Story"];
                }
                parentDictType = ParentDictType.Story;

            }
            else
            {
                if (isRDrama)
                {
                    parentDict = (PListArray)rootDict["Council"];
                    parentDictType = ParentDictType.Council;
                }
                else
                {
                    parentDict = (PListArray)rootDict["Trigger"]; 
                    parentDictType = ParentDictType.Trigger;
                }
            }
            //Skip Sections Amount Byte
            int sectionOffset = sceneOffset+2;
            //Traversing Sections
            for (int i = 0; i != sectionAmount; ++i)
            {
                //Create new secion/scene
                PListDict sectionDict = CreatSectionDict(parentDictType);
                parentDict.Add(sectionDict);
                //Skip section  bytes segment
                int curOffset = sectionOffset + 2;
                //Read Test/Info List
                Console.WriteLine("Test/Info Begin");
                while (GetInt16Data(curOffset) != 0)
                {
                    int instructID = GetInt16Data(curOffset);
                    CodeStyleUnit codeStyle = styleDic[instructID];
                    Console.WriteLine(instructID + "-" + codeStyle.instructDspt);
                    List<String> paramValueList = new List<string>();
                    paramValueList = GetParamValueList(curOffset);
                    curOffset = FindNextInstructOffset(curOffset);
                    //3----1.Story's Scene   2.Trigger's Section    3.Council.Section
                    //Story    only accept Info
                    if (parentDictType == ParentDictType.Story && instructID==2)
                    {
                        sectionDict["Information"] = new PListString(paramValueList[0]);
                    }
                    //Trigger   info add to infomation;other add to condition
                    else if (parentDictType == ParentDictType.Trigger)
                    {
                        if (instructID == 2)
                            sectionDict["Information"] = new PListString(paramValueList[0]);
                        else
                        {
                            PListArray conditions = (PListArray)sectionDict["Condition"];
                            PListDict elem = instructMap.MappingTestInstruct(instructID,paramValueList);
                            conditions.Add(elem);
                        }


                    }
                    //Council only accept Info and hero touch
                    else if (parentDictType == ParentDictType.Council)
                    {
                        if (instructID == 2)
                            sectionDict["Information"] = new PListString(paramValueList[0]);
                        else if(instructID==45)
                        {
                            sectionDict["HeroID"] = new PListInteger(long.Parse(paramValueList[0]));
                        }
                    }

                }
                //Skip end tag
                curOffset += 2;
                Console.WriteLine("Test/Info End");

                //Read Instruct List
                int maxOffset = curOffset + GetInt16Data(curOffset);
                //Skip Amount Bytes Segment
                curOffset += 2;
                Console.WriteLine("Dramas Begin");
                PListArray dramas = (PListArray)sectionDict["Dramas"];
                dramas.Add(AddDramaDialog.CreateInstructDict(kInstructType.kInstructTypeDramaBegin));
                while (curOffset < maxOffset)
                {

                    curOffset = ReadInstruct(curOffset,dramas);

                }
                dramas.Add(AddDramaDialog.CreateInstructDict(kInstructType.kInstructTypeDramaOver));
                Console.WriteLine("Dramas End");
                Console.WriteLine("Section End");
                //Move to next section
                sectionOffset +=(GetInt16Data(sectionOffset)+2);

            }
        }
        private int FindNextInstructOffset(int curOffset)
        {
            int instructID = GetInt16Data(curOffset);
            CodeStyleUnit codeStyle = styleDic[instructID];
            if (instructID == 70 || instructID == 71)
            {
                //Need Add code
                curOffset += codeStyle.instructBytes;
                return curOffset;
            }
            else if (instructID == 5)
            {
                //-Skip Instruct ID and 35 parma
                curOffset += 4;
                int varsAmount = GetInt16Data(curOffset);
                //-Skip to var list segment
                curOffset += 2;
                for (int i = 0; i < varsAmount; ++i)
                {
                    int variable = GetInt16Data(curOffset);
                    curOffset += 2;
                }
                //-Skip to amount byte
                curOffset += 2;
                varsAmount = GetInt16Data(curOffset);
                //-Skip to var list segment
                curOffset += 2;
                //distinguish true vars with false vars
                for (int i = 0; i < varsAmount; ++i)
                {
                    int variable = GetInt16Data(curOffset);
                    curOffset += 2;
                }
                return curOffset;

            }
            //Process common instruct
            //-Skip Instruct ID
            curOffset += 2;
            for (int index = 0; index < codeStyle.paramList.Count; ++index)
            {

                string param = codeStyle.paramList[index];
                if (index % 2 == 0)
                {
                    //Parma ID
                    //Skip Param ID
                    curOffset += 2;
                    if (Int32.Parse(param) == 4)
                    {
                        //Int32 Value
                        int value = GetInt32Data(curOffset);
                        curOffset += 4;
                    }
                    else if (Int32.Parse(param) == 5)
                    {
                        //String Value
                        String sValue = GetStringData(curOffset);
                        int strBytecount = Encoding.Default.GetByteCount(sValue);
                        curOffset += (strBytecount + 1);
                    }
                    else
                    {
                        int value = GetInt16Data(curOffset);
                        //Int16 Value
                        curOffset += 2;
                    }
                }
                else
                {
                    //Parma Dspt

                }
            }
            return curOffset;
        }
        private int ReadInstruct(int curOffset,PListArray dictArray){
            int instructID = GetInt16Data(curOffset);
            CodeStyleUnit codeStyle = styleDic[instructID];
            Console.WriteLine(instructID + "-" + codeStyle.instructDspt);
            int synResult=ProcessSynInstructs(curOffset,codeStyle,dictArray);
            if (synResult != -1)
                return synResult;
            if (instructID == 1)
            {
                //Skip to test/Info instruct
                curOffset += 2;
                //Test/Info ID
                instructID = GetInt16Data(curOffset);
                if (isTestInstruct(instructID))
                {
                    List<String> paramValueList = GetParamValueList(curOffset);
                    PListDict testInsrtuctDict = AddDramaDialog.CreateInstructDict(kInstructType.kInstructTypeConditionTest);
                    dictArray.Add(testInsrtuctDict);
                    //内部信息指令直接填写到Information中，其他测试指令添加进Condition
                    if (instructID == 2) {
                        testInsrtuctDict["Information"] = new PListString(paramValueList[0]);
                    }
                    else
                    {
                        PListDict logicalTest = instructMap.MappingTestInstruct(instructID, paramValueList);
                        ((PListArray)testInsrtuctDict["Condition"]).Add(logicalTest);
                    }
                    curOffset = FindNextInstructOffset(curOffset);
                    curOffset += 2;
                    //读取测试通过的指令集
                    PListArray subDrama1 = (PListArray)testInsrtuctDict["SubDrama1"];
                    subDrama1.Clear();
                    subDrama1.Add(AddDramaDialog.CreateInstructDict(kInstructType.kInstructTypeDramaBegin));
                    while (GetInt16Data(curOffset) != 0)
                    {
                        curOffset = ReadInstruct(curOffset, subDrama1);
                    }
                    subDrama1.Add(AddDramaDialog.CreateInstructDict(kInstructType.kInstructTypeDramaOver));

                    //Skip End
                    curOffset += 2;
                    //Check if need add else
                    //读取测试未通过的指令集
                    if (GetInt16Data(curOffset) == 1 && GetInt16Data(curOffset + 2) == 3)
                    {
                        curOffset += 6;
                        PListArray subDrama2 = (PListArray)testInsrtuctDict["SubDrama2"];
                        subDrama2.Clear();
                        subDrama2.Add(AddDramaDialog.CreateInstructDict(kInstructType.kInstructTypeDramaBegin));
                   
                        while (GetInt16Data(curOffset) != 0)
                        {
                            curOffset = ReadInstruct(curOffset, (PListArray)testInsrtuctDict["SubDrama2"]);
                        }
                        subDrama2.Add(AddDramaDialog.CreateInstructDict(kInstructType.kInstructTypeDramaOver));

                        curOffset += 2;
                    }
                }
                else if (instructID == 19)
                {
                    //Skip case Instruct
                    curOffset = FindNextInstructOffset(curOffset);
                    //Skip Amount
                    curOffset += 2;
                    while (GetInt16Data(curOffset) != 0)
                    {
                        curOffset = ReadInstruct(curOffset,dictArray);
                    }
                    //Skip end
                    curOffset += 2;
                }
                return curOffset;
            }
            else if (instructID == 18)
            {
                List<String> paramValueList = GetParamValueList(curOffset);
                PListDict interactionDict = AddDramaDialog.CreateInstructDict(kInstructType.kInstructTypeInteraction);
                dictArray.Add(interactionDict);
                //填写Word1,2,3参数
                String paramWord = paramValueList[0];
                String[] words = paramWord.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
                int i = 1;
                foreach (String word in words)
                {
                    interactionDict["Word" + (i++)] = new PListString(word);
                }
                curOffset = FindNextInstructOffset(curOffset);
                i = 1;
                //Skip 00，为了防止光荣的任性0000
                if (isRDrama)
                    curOffset += 2;
                else if (GetInt16Data(curOffset) == 0 && GetInt16Data(curOffset + 2) == 1 && GetInt16Data(curOffset + 4)==19)
                    curOffset += 2;
                while (GetInt16Data(curOffset) == 1 && GetInt16Data(curOffset + 2) == 19)
                {
                    PListArray subDrama = (PListArray)interactionDict["SubDrama" + i];
                    subDrama.Clear();
                    subDrama.Add(AddDramaDialog.CreateInstructDict(kInstructType.kInstructTypeDramaBegin));
                   
                    curOffset = ReadInstruct(curOffset, (PListArray)interactionDict["SubDrama"+(i++)]);
                    subDrama.Add(AddDramaDialog.CreateInstructDict(kInstructType.kInstructTypeDramaOver));

                }
                return curOffset;

            }
            List<PListDict> commonInstructs = instructMap.MappingCommonInstruct(instructID, GetParamValueList(curOffset));
            foreach(PListDict dict in commonInstructs)
                dictArray.Add(dict);
            return FindNextInstructOffset(curOffset);
        }
        private int ProcessSynInstructs(int curOffset, CodeStyleUnit codeStyle, PListArray dictArray)
        {
            int instructID = GetInt16Data(curOffset);
            int[] needSynIDs = new int[] {48, 50, 51, 52 };
            foreach (int numSyn in needSynIDs)
            {
                if (instructID == numSyn && isRDrama)
                {
                    int i = 1;
                    while (GetInt16Data(curOffset + codeStyle.instructBytes * i) == numSyn)
                    {

                        ++i;
                    }
                    if (i > 1)
                    {
                        PListDict synchronizeDict = AddDramaDialog.CreateInstructDict(kInstructType.kInstructTypeSynchronization);
                        synchronizeDict["Nums"] = new PListInteger(i);
                        dictArray.Add(synchronizeDict);
                        for (int index = 0; index < i; ++index)
                        {

                            List<PListDict> commonInstructs_ = instructMap.MappingCommonInstruct(instructID, GetParamValueList(curOffset));
                            foreach (PListDict dict in commonInstructs_)
                                dictArray.Add(dict);
                            curOffset += codeStyle.instructBytes;
                        }
                        return curOffset;
                    }
                }
            }
            return -1;
        }
        private List<String> GetParamValueList(int curOffset)
        {
            int instructID = GetInt16Data(curOffset);
            List<String> paramValueList = new List<string>();
            CodeStyleUnit codeStyle = styleDic[instructID];
            if (instructID == 70 || instructID == 71)
            {
                int maxOffset = codeStyle.instructBytes + curOffset;
                curOffset += 2;
                while (curOffset < maxOffset)
                {
                    curOffset = ReadParamValue(codeStyle, curOffset, paramValueList);
                }
                return paramValueList;
            }
            else if (instructID == 5)
            {
                //-Skip Instruct ID and 35 parma
                curOffset += 4;
                int varsAmount = GetInt16Data(curOffset);
                //-Skip to var list segment
                curOffset += 2;
                for (int i = 0; i < varsAmount; ++i)
                {
                    int variable = GetInt16Data(curOffset);
                    paramValueList.Add("" + variable);
                    curOffset += 2;
                }
                //-Skip to amount byte
                curOffset += 2;
                paramValueList.Add("-1" );

                varsAmount = GetInt16Data(curOffset);
                //-Skip to var list segment
                curOffset += 2;
                //distinguish true vars with false vars
                for (int i = 0; i < varsAmount; ++i)
                {
                    int variable = GetInt16Data(curOffset);
                    paramValueList.Add("" + variable);
                    curOffset += 2;
                }
                return paramValueList;

            }
            //Process common instruct
            //-Skip Instruct ID
            curOffset += 2;
            curOffset = ReadParamValue(codeStyle, curOffset, paramValueList);
            return paramValueList;
        }
        private int ReadParamValue(CodeStyleUnit codeStyle, int curOffset, List<String> paramValueList)
        {
            for (int index = 0; index < codeStyle.paramList.Count; ++index)
            {

                string param = codeStyle.paramList[index];
                if (index % 2 == 0)
                {
                    //Parma ID
                    //Skip Param ID
                    curOffset += 2;
                    if (Int32.Parse(param) == 4)
                    {
                        //Int32 Value
                        int value = GetInt32Data(curOffset);
                        paramValueList.Add("" + value);
                        curOffset += 4;
                    }
                    else if (Int32.Parse(param) == 5)
                    {
                        //String Value
                        String sValue = GetStringData(curOffset);
                        paramValueList.Add(sValue);
                        Console.WriteLine("文字：" + sValue);
                        int strBytecount = Encoding.Default.GetByteCount(sValue);
                        curOffset += (strBytecount + 1);
                    }
                    else
                    {
                        int value = GetInt16Data(curOffset);
                        paramValueList.Add("" + value);
                        //Int16 Value
                        curOffset += 2;

                    }
                }
                else
                {
                    //Parma Dspt

                }
            }
            return curOffset;
        }
        
        private PListDict CreatSectionDict(ParentDictType parentDictType)
        {
            PListDict scene = new PListDict();
            scene.Add("Information", new PListString("内部信息"));
            PListArray dramas = new PListArray();
            scene.Add("Dramas", dramas);
            if (parentDictType == ParentDictType.Council)
            {
                scene.Add("HeroID", new PListInteger(0));
            }
            else if (parentDictType == ParentDictType.Trigger)
            {
                PListArray conditions = new PListArray();
                scene.Add("Condition", conditions);
            }
            return scene;
        }
        private bool isTestInstruct(int insrtuctID)
        {
            if (insrtuctID==2|| insrtuctID == 5 || insrtuctID == 37 ||
                insrtuctID == 38 || insrtuctID == 46 ||
                insrtuctID == 54 || insrtuctID == 55 ||
                insrtuctID == 63 || insrtuctID == 64 || 
                insrtuctID == 65 || insrtuctID == 66 || insrtuctID == 67)
            {
                return true;
            }
            else
                return false;
        }

        #region Data Getting Function
        private byte[] GetData(int offset,int count){
            var value = new byte[count];
            CurrentStream.Seek(offset, SeekOrigin.Begin);
            CurrentStream.Read(value, 0, count);
            return value;
        }
        private byte GetByteData(int offset)
        {
            var value = GetData(offset, 1);
            return value[0];

        }
        private short GetInt16Data(int offset)
        {
            var value = GetData(offset, 2);
            return BitConverter.ToInt16(value, 0);
        }
        private short GetInt32Data(int offset)
        {
            var value = GetData(offset, 4);
            return BitConverter.ToInt16(value, 0);
        }
        private String GetStringData(int offset)
        {
            String s="";
            var byte1 = new byte[1];
            var byte2 = new byte[1];

            CurrentStream.Seek(offset, SeekOrigin.Begin);
            while (true)
            {
                
                CurrentStream.Read(byte1, 0, 1);
                if (byte1[0] == 0)
                {
                    break;
                }
                else if (byte1[0] <= 127&&byte1[0]>0)
                {
                    s += System.Text.Encoding.Default.GetString(byte1);
                }
                else
                {
                    CurrentStream.Read(byte2, 0, 1);
                    var bytes = new byte[2];
                    bytes[0] = byte1[0];
                    bytes[1] = byte2[0];
                    s += System.Text.Encoding.GetEncoding("gb2312").GetString(bytes);
                }
            }
            return s;
        }
        #endregion
        #region FileStream Function
        public FileStream OpenRead()
        {
            return CurrentFile == null ? null : CurrentFile.OpenRead();
        }

        public FileStream OpenWrite()
        {
            return CurrentFile == null ? null : CurrentFile.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
        }

        public FileStream Open(FileAccess access)
        {
            return CurrentFile == null ? null : CurrentFile.Open(FileMode.Open, access, FileShare.ReadWrite);
        }

        public FileStream Open(FileShare share)
        {
            return CurrentFile == null ? null : CurrentFile.Open(FileMode.Open, FileAccess.ReadWrite, share);
        }

        public FileStream Open(FileAccess access, FileShare share)
        {
            return CurrentFile == null ? null : CurrentFile.Open(FileMode.Open, access, share);
        }
    #endregion
    }
}
